/*
 * This file is part of TradeHelper library.
 *
 * TradeHelper library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TradeHelper library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TradeHelper library.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.rag.forum.ui;

import com.alee.extended.layout.WrapFlowLayout;
import com.alee.laf.StyleConstants;
import com.alee.laf.panel.WebPanel;
import com.alee.laf.scroll.WebScrollPane;
import com.alee.managers.hotkey.Hotkey;
import com.alee.managers.hotkey.HotkeyManager;
import com.alee.managers.hotkey.HotkeyRunnable;
import com.alee.utils.LafUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.collection.ValuesTable;
import com.alee.utils.swing.Timer;
import com.rag.core.Item;
import com.rag.managers.RagManager;
import com.rag.utils.RagUtils;

import javax.swing.FocusManager;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;

/**
 * User: mgarin Date: 12.04.12 Time: 17:53
 */

public class ItemDropList extends WebPanel
{
    private static final BasicStroke dashStroke =
            new BasicStroke ( 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f,
                    new float[]{ 8f, 8f }, 0f );

    public int dashRound = StyleConstants.smallRound;
    public int dashSideSpacing = 10;

    private Color dropBackground = new Color ( 242, 242, 242 );
    private Color dropBorder = new Color ( 192, 192, 192 );

    private List<ItemSelectionListener> listeners = new ArrayList<ItemSelectionListener> ();

    private ValuesTable<Item, ItemPlate> items;

    private int preferredWidth = 1;
    private int visibleRowCount = 4;

    private float dropFilesLabelOpacity = 1f;

    private WebScrollPane view;

    private String dropText;

    public ItemDropList ()
    {
        super ();

        setMargin ( 8 );

        WrapFlowLayout layout = new WrapFlowLayout ( false, 8, 8 );
        layout.setHalign ( SwingConstants.CENTER );
        layout.setValign ( SwingConstants.CENTER );
        setLayout ( layout );

        setFocusable ( true );
        setFont ( SwingUtils.getDefaultLabelFont ().deriveFont ( Font.BOLD ).deriveFont ( 20f ) );

        // Текст пустого поля сброса
        dropText = null;

        // Список итемов
        items = new ValuesTable<Item, ItemPlate> ();

        // Слушатель перетаскивания
        setTransferHandler ( new TransferHandler ()
        {
            public boolean canImport ( TransferHandler.TransferSupport info )
            {
                return isDropEnabled ();
            }

            public boolean importData ( TransferHandler.TransferSupport info )
            {
                return info.isDrop () && importData ( info.getTransferable () );
            }

            public boolean importData ( Transferable t )
            {
                return isDropEnabled () && itemsImported ( t );
            }

            protected boolean isDropEnabled ()
            {
                return ItemDropList.this.isEnabled ();
            }

            protected boolean itemsImported ( Transferable t )
            {
                Item item = RagUtils.getItem ( t );
                addItem ( item );
                return item != null;
            }
        } );

        // Слушатель вставки
        HotkeyManager.registerHotkey ( this, this, Hotkey.CTRL_V, new HotkeyRunnable ()
        {
            public void run ( KeyEvent e )
            {
                addItem ( RagUtils.getClipboardItem () );
            }
        }, true );

        // Слушатель вставки
        HotkeyManager.registerHotkey ( this, this, Hotkey.CTRL_C, new HotkeyRunnable ()
        {
            public void run ( KeyEvent e )
            {
                Component fo = FocusManager.getCurrentManager ().getFocusOwner ();
                if ( fo instanceof ItemPlate )
                {
                    final ItemPlate ip = ( ItemPlate ) fo;
                    RagUtils.copyToClipboard ( ip.getItem () );
                }
            }
        }, true );

        // Слушатель вырезания
        HotkeyManager.registerHotkey ( this, this, Hotkey.CTRL_X, new HotkeyRunnable ()
        {
            public void run ( KeyEvent e )
            {
                Component fo = FocusManager.getCurrentManager ().getFocusOwner ();
                if ( fo instanceof ItemPlate )
                {
                    final ItemPlate ip = ( ItemPlate ) fo;
                    RagUtils.copyToClipboard ( ip.getItem () );
                    ip.deleteItem ();
                }
            }
        }, true );

        // Слушатель удаления
        HotkeyManager.registerHotkey ( this, this, Hotkey.DELETE, new HotkeyRunnable ()
        {
            public void run ( KeyEvent e )
            {
                Component fo = FocusManager.getCurrentManager ().getFocusOwner ();
                if ( fo instanceof ItemPlate )
                {
                    ( ( ItemPlate ) fo ).deleteItem ();
                }
            }
        }, true );

        // Для поимки фокуса на первый компонент
        addMouseListener ( new MouseAdapter ()
        {
            public void mousePressed ( MouseEvent e )
            {
                if ( ItemDropList.this.getComponentCount () > 0 )
                {
                    ItemDropList.this.transferFocus ();
                }
                else
                {
                    ItemDropList.this.requestFocusInWindow ();
                }
            }
        } );

        // Для анимации фона
        addItemSelectionListener ( new ItemSelectionListener ()
        {
            private int filesCount = 0;
            private Timer animator = null;

            public void selectionChanged ( List<Item> items )
            {
                if ( filesCount == 0 && items.size () > 0 )
                {
                    stopAnimator ();
                    filesCount = items.size ();
                    animator = new Timer ( 1000 / StyleConstants.animationFps, new ActionListener ()
                    {
                        public void actionPerformed ( ActionEvent e )
                        {
                            if ( dropFilesLabelOpacity > 0f )
                            {
                                dropFilesLabelOpacity -= 0.1f;
                            }
                            dropFilesLabelOpacity = Math.max ( dropFilesLabelOpacity, 0f );
                            ItemDropList.this.repaint ();
                            if ( dropFilesLabelOpacity <= 0f )
                            {
                                animator.stop ();
                            }
                        }
                    } );
                    animator.start ();
                }
                else if ( filesCount > 0 && items.size () == 0 )
                {
                    stopAnimator ();
                    filesCount = items.size ();
                    animator = new Timer ( 1000 / StyleConstants.animationFps, new ActionListener ()
                    {
                        public void actionPerformed ( ActionEvent e )
                        {
                            if ( dropFilesLabelOpacity < 1f )
                            {
                                dropFilesLabelOpacity += 0.1f;
                            }
                            dropFilesLabelOpacity = Math.min ( dropFilesLabelOpacity, 1f );
                            ItemDropList.this.repaint ();
                            if ( dropFilesLabelOpacity >= 1f )
                            {
                                animator.stop ();
                            }
                        }
                    } );
                    animator.start ();
                }
            }

            private void stopAnimator ()
            {
                if ( animator != null && animator.isRunning () )
                {
                    animator.stop ();
                }
            }
        } );
    }

    public int getPreferredWidth ()
    {
        return preferredWidth;
    }

    public void setPreferredWidth ( int preferredWidth )
    {
        this.preferredWidth = preferredWidth;
    }

    public WebScrollPane getView ( boolean drawBorder )
    {
        if ( view == null )
        {
            view = new WebScrollPane ( ItemDropList.this, drawBorder )
            {
                public Dimension getPreferredSize ()
                {
                    Insets insets = ItemDropList.this.getInsets ();
                    WrapFlowLayout layout = ( WrapFlowLayout ) ItemDropList.this.getLayout ();
                    return new Dimension ( preferredWidth,
                            26 * visibleRowCount + layout.getVgap () * ( visibleRowCount - 1 ) +
                                    insets.top + insets.bottom );
                }
            };
            view.setDrawFocus ( false );
        }
        else if ( view.isDrawBorder () != drawBorder )
        {
            view.setDrawBorder ( drawBorder );
        }
        return view;
    }

    public String getDropText ()
    {
        return dropText;
    }

    public void setDropText ( String dropText )
    {
        this.dropText = dropText;
    }

    public int getVisibleRowCount ()
    {
        return visibleRowCount;
    }

    public void setVisibleRowCount ( int visibleRowCount )
    {
        this.visibleRowCount = visibleRowCount;
        if ( view != null )
        {
            view.revalidate ();
        }
    }

    public void addItems ( List<Item> items )
    {
        if ( items != null && items.size () > 0 )
        {
            // Исключаем повторные итемы из списка
            for ( int i = items.size () - 1; i >= 0; i-- )
            {
                for ( Item existing : ItemDropList.this.items.getKeys () )
                {
                    if ( existing.getId ().equals ( items.get ( i ).getId () ) )
                    {
                        items.remove ( i );
                        break;
                    }
                }
            }
            if ( items.size () == 0 )
            {
                return;
            }

            // Добавляем итем
            ItemPlate itemPlate = null;
            for ( Item item : items )
            {
                itemPlate = new ItemPlate ( item );
                ItemDropList.this.add ( itemPlate );
                ItemDropList.this.items.put ( item, itemPlate );
            }

            // Обновляем список
            ItemDropList.this.revalidate ();
            ItemDropList.this.repaint ();
            itemPlate.requestFocusInWindow ();

            // Оповещаем об изменениях
            fireSelectionChanged ( ItemDropList.this.items.getKeys () );
        }
    }

    public void addItem ( String itemId )
    {
        addItem ( RagManager.getItem ( itemId ) );
    }

    public void addItem ( Item item )
    {
        if ( item != null )
        {
            // Исключаем повторные итемы из списка
            for ( Item existing : items.getKeys () )
            {
                if ( existing.getId ().equals ( item.getId () ) )
                {
                    // Передаём фокус существующему итему и отменяем операцию
                    items.get ( existing ).requestFocusInWindow ();
                    return;
                }
            }

            // Добавляем итем
            ItemPlate itemPlate = new ItemPlate ( item );
            ItemDropList.this.add ( itemPlate );
            items.put ( item, itemPlate );

            // Обновляем список
            ItemDropList.this.revalidate ();
            ItemDropList.this.repaint ();
            itemPlate.requestFocusInWindow ();

            // Оповещаем об изменениях
            fireSelectionChanged ( items.getKeys () );
        }
    }

    public void deleteItem ( Item item )
    {
        deleteItem ( items.get ( item ) );
    }

    public void deleteItem ( ItemPlate itemPlate )
    {
        if ( itemPlate != null )
        {
            itemPlate.deleteItem ();
        }
    }

    protected void removeItem ( Item item )
    {
        if ( item != null )
        {
            // Удаляем итем
            ItemDropList.this.remove ( items.get ( item ) );
            ItemDropList.this.revalidate ();
            ItemDropList.this.repaint ();
            items.remove ( item );

            // Оповещаем об изменениях
            fireSelectionChanged ( items.getKeys () );

            // Корректно передаём фокус
            if ( items.size () == 0 )
            {
                ItemDropList.this.requestFocusInWindow ();
            }
        }
    }

    public ValuesTable<Item, ItemPlate> getItems ()
    {
        return items;
    }

    public List<String> getItemIds ()
    {
        List<String> ids = new ArrayList<String> ();
        for ( Item item : items.getKeys () )
        {
            ids.add ( item.getId () );
        }
        return ids;
    }

    protected void paintComponent ( Graphics g )
    {
        super.paintComponent ( g );

        if ( dropFilesLabelOpacity > 0f )
        {
            Graphics2D g2d = ( Graphics2D ) g;
            g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON );

            Composite old = LafUtils.setupAlphaComposite ( g2d, dropFilesLabelOpacity );

            g2d.setPaint ( dropBackground );
            g2d.fillRoundRect ( dashSideSpacing + Math.round ( dashStroke.getLineWidth () / 2 ),
                    dashSideSpacing + Math.round ( dashStroke.getLineWidth () / 2 ),
                    getWidth () - dashSideSpacing * 2 - Math.round ( dashStroke.getLineWidth () ),
                    getHeight () - dashSideSpacing * 2 - Math.round ( dashStroke.getLineWidth () ),
                    dashRound * 2, dashRound * 2 );

            g2d.setPaint ( dropBorder );

            if ( dropText != null )
            {
                FontMetrics fm = g2d.getFontMetrics ();
                g2d.drawString ( dropText, getWidth () / 2 - fm.stringWidth ( dropText ) / 2,
                        getHeight () / 2 + fm.getAscent () / 2 );
            }

            Stroke stroke = g2d.getStroke ();
            g2d.setStroke ( dashStroke );
            g2d.drawRoundRect ( dashSideSpacing, dashSideSpacing,
                    getWidth () - dashSideSpacing * 2 - 1, getHeight () - dashSideSpacing * 2 - 1,
                    dashRound * 2, dashRound * 2 );
            g2d.setStroke ( stroke );

            LafUtils.restoreComposite ( g2d, old );
        }
    }

    public void addItemSelectionListener ( ItemSelectionListener listener )
    {
        listeners.add ( listener );
    }

    public void removeItemSelectionListener ( ItemSelectionListener listener )
    {
        listeners.remove ( listener );
    }

    private void fireSelectionChanged ( List<Item> selectedItems )
    {
        for ( ItemSelectionListener listener : listeners )
        {
            listener.selectionChanged ( selectedItems );
        }
    }
}